home *** CD-ROM | disk | FTP | other *** search
- /* Buffer management for tar.
- Copyright (C) 1988, 1992, 1993 Free Software Foundation
-
- This file is part of GNU Tar.
-
- GNU Tar is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- GNU Tar is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNU Tar; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /*
- * Buffer management for tar.
- *
- * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
- */
-
- #include <stdio.h>
- #include <errno.h>
- #ifndef STDC_HEADERS
- extern int errno;
- #endif
- #include <sys/types.h> /* For non-Berkeley systems */
- #include <signal.h>
- #include <time.h>
- time_t time ();
-
- #ifdef HAVE_SYS_MTIO_H
- #include <sys/ioctl.h>
- #include <sys/mtio.h>
- #endif
-
- #ifdef BSD42
- #include <sys/file.h>
- #else
- #ifndef V7
- #include <fcntl.h>
- #endif
- #endif
-
- #ifdef __MSDOS__
- #include <process.h>
- #endif
-
- #ifdef XENIX
- #include <sys/inode.h>
- #endif
-
- #include "tar.h"
- #include "port.h"
- #include "rmt.h"
- #include "regex.h"
-
- /* Either stdout or stderr: The thing we write messages (standard msgs, not
- errors) to. Stdout unless we're writing a pipe, in which case stderr */
- FILE *msg_file = stdout;
-
- #define STDIN 0 /* Standard input file descriptor */
- #define STDOUT 1 /* Standard output file descriptor */
-
- #define PREAD 0 /* Read file descriptor from pipe() */
- #define PWRITE 1 /* Write file descriptor from pipe() */
-
- #define MAGIC_STAT 105 /* Magic status returned by child, if
- it can't exec. We hope compress/sh
- never return this status! */
-
- void *valloc ();
-
- void writeerror ();
- void readerror ();
-
- void ck_pipe ();
- void ck_close ();
-
- int backspace_output ();
- extern void finish_header ();
- void flush_archive ();
- int isfile ();
- int new_volume ();
- void verify_volume ();
- extern void to_oct ();
-
- #ifndef __MSDOS__
- /* Obnoxious test to see if dimwit is trying to dump the archive */
- dev_t ar_dev;
- ino_t ar_ino;
- #endif
-
- /*
- * The record pointed to by save_rec should not be overlaid
- * when reading in a new tape block. Copy it to record_save_area first, and
- * change the pointer in *save_rec to point to record_save_area.
- * Saved_recno records the record number at the time of the save.
- * This is used by annofile() to print the record number of a file's
- * header record.
- */
- static union record **save_rec;
- union record record_save_area;
- static long saved_recno;
-
- /*
- * PID of child program, if f_compress or remote archive access.
- */
- static int childpid = 0;
-
- /*
- * Record number of the start of this block of records
- */
- long baserec;
-
- /*
- * Error recovery stuff
- */
- static int r_error_count;
-
- /*
- * Have we hit EOF yet?
- */
- static int hit_eof;
-
- /* Checkpointing counter */
- static int checkpoint;
-
- /* JF we're reading, but we just read the last record and its time to update */
- extern time_to_start_writing;
- int file_to_switch_to = -1; /* If remote update, close archive, and use
- this descriptor to write to */
-
- static int volno = 1; /* JF which volume of a multi-volume tape
- we're on */
- static int global_volno = 1; /* Volume number to print in external messages. */
-
- char *save_name = 0; /* Name of the file we are currently writing */
- long save_totsize; /* total size of file we are writing. Only
- valid if save_name is non_zero */
- long save_sizeleft; /* Where we are in the file we are writing.
- Only valid if save_name is non-zero */
-
- int write_archive_to_stdout;
-
- /* Used by fl_read and fl_write to store the real info about saved names */
- static char real_s_name[NAMSIZ];
- static long real_s_totsize;
- static long real_s_sizeleft;
-
- /* Reset the EOF flag (if set), and re-set ar_record, etc */
-
- void
- reset_eof ()
- {
- if (hit_eof)
- {
- hit_eof = 0;
- ar_record = ar_block;
- ar_last = ar_block + blocking;
- ar_reading = 0;
- }
- }
-
- /*
- * Return the location of the next available input or output record.
- * Return NULL for EOF. Once we have returned NULL, we just keep returning
- * it, to avoid accidentally going on to the next file on the "tape".
- */
- union record *
- findrec ()
- {
- if (ar_record == ar_last)
- {
- if (hit_eof)
- return (union record *) NULL; /* EOF */
- flush_archive ();
- if (ar_record == ar_last)
- {
- hit_eof++;
- return (union record *) NULL; /* EOF */
- }
- }
- return ar_record;
- }
-
-
- /*
- * Indicate that we have used all records up thru the argument.
- * (should the arg have an off-by-1? XXX FIXME)
- */
- void
- userec (rec)
- union record *rec;
- {
- while (rec >= ar_record)
- ar_record++;
- /*
- * Do NOT flush the archive here. If we do, the same
- * argument to userec() could mean the next record (if the
- * input block is exactly one record long), which is not what
- * is intended.
- */
- if (ar_record > ar_last)
- abort ();
- }
-
-
- /*
- * Return a pointer to the end of the current records buffer.
- * All the space between findrec() and endofrecs() is available
- * for filling with data, or taking data from.
- */
- union record *
- endofrecs ()
- {
- return ar_last;
- }
-
-
- /*
- * Duplicate a file descriptor into a certain slot.
- * Equivalent to BSD "dup2" with error reporting.
- */
- void
- dupto (from, to, msg)
- int from, to;
- char *msg;
- {
- int err;
-
- if (from != to)
- {
- err = close (to);
- if (err < 0 && errno != EBADF)
- {
- msg_perror ("Cannot close descriptor %d", to);
- exit (EX_SYSTEM);
- }
- err = dup (from);
- if (err != to)
- {
- msg_perror ("cannot dup %s", msg);
- exit (EX_SYSTEM);
- }
- ck_close (from);
- }
- }
-
- #ifdef __MSDOS__
- void
- child_open ()
- {
- fprintf (stderr, "MS-DOS %s can't use compressed or remote archives\n", tar);
- exit (EX_ARGSBAD);
- }
-
- #else
- void
- child_open ()
- {
- int pipe[2];
- int err = 0;
-
- int kidpipe[2];
- int kidchildpid;
-
- #define READ 0
- #define WRITE 1
-
- ck_pipe (pipe);
-
- childpid = fork ();
- if (childpid < 0)
- {
- msg_perror ("cannot fork");
- exit (EX_SYSTEM);
- }
- if (childpid > 0)
- {
- /* We're the parent. Clean up and be happy */
- /* This, at least, is easy */
-
- if (ar_reading)
- {
- f_reblock++;
- archive = pipe[READ];
- ck_close (pipe[WRITE]);
- }
- else
- {
- archive = pipe[WRITE];
- ck_close (pipe[READ]);
- }
- return;
- }
-
- /* We're the kid */
- if (ar_reading)
- {
- dupto (pipe[WRITE], STDOUT, "(child) pipe to stdout");
- ck_close (pipe[READ]);
- }
- else
- {
- dupto (pipe[READ], STDIN, "(child) pipe to stdin");
- ck_close (pipe[WRITE]);
- }
-
- /* We need a child tar only if
- 1: we're reading/writing stdin/out (to force reblocking)
- 2: the file is to be accessed by rmt (compress doesn't know how)
- 3: the file is not a plain file */
- #ifdef NO_REMOTE
- if (!(ar_files[0][0] == '-' && ar_files[0][1] == '\0') && isfile (ar_files[0]))
- #else
- if (!(ar_files[0][0] == '-' && ar_files[0][1] == '\0') && !_remdev (ar_files[0]) && isfile (ar_files[0]))
- #endif
- {
- /* We don't need a child tar. Open the archive */
- if (ar_reading)
- {
- archive = open (ar_files[0], O_RDONLY | O_BINARY, 0666);
- if (archive < 0)
- {
- msg_perror ("can't open archive %s", ar_files[0]);
- exit (EX_BADARCH);
- }
- dupto (archive, STDIN, "archive to stdin");
- /* close(archive); */
- }
- else
- {
- archive = creat (ar_files[0], 0666);
- if (archive < 0)
- {
- msg_perror ("can't open archive %s", ar_files[0]);
- exit (EX_BADARCH);
- }
- dupto (archive, STDOUT, "archive to stdout");
- /* close(archive); */
- }
- }
- else
- {
- /* We need a child tar */
- ck_pipe (kidpipe);
-
- kidchildpid = fork ();
- if (kidchildpid < 0)
- {
- msg_perror ("child can't fork");
- exit (EX_SYSTEM);
- }
-
- if (kidchildpid > 0)
- {
- /* About to exec compress: set up the files */
- if (ar_reading)
- {
- dupto (kidpipe[READ], STDIN, "((child)) pipe to stdin");
- ck_close (kidpipe[WRITE]);
- /* dup2(pipe[WRITE],STDOUT); */
- }
- else
- {
- /* dup2(pipe[READ],STDIN); */
- dupto (kidpipe[WRITE], STDOUT, "((child)) pipe to stdout");
- ck_close (kidpipe[READ]);
- }
- /* ck_close(pipe[READ]); */
- /* ck_close(pipe[WRITE]); */
- /* ck_close(kidpipe[READ]);
- ck_close(kidpipe[WRITE]); */
- }
- else
- {
- /* Grandchild. Do the right thing, namely sit here and
- read/write the archive, and feed stuff back to compress */
- tar = "tar (child)";
- if (ar_reading)
- {
- dupto (kidpipe[WRITE], STDOUT, "[child] pipe to stdout");
- ck_close (kidpipe[READ]);
- }
- else
- {
- dupto (kidpipe[READ], STDIN, "[child] pipe to stdin");
- ck_close (kidpipe[WRITE]);
- }
-
- if (ar_files[0][0] == '-' && ar_files[0][1] == '\0')
- {
- if (ar_reading)
- archive = STDIN;
- else
- archive = STDOUT;
- }
- else /* This can't happen if (ar_reading==2)
- archive = rmtopen(ar_files[0], O_RDWR|O_CREAT|O_BINARY, 0666);
- else */ if (ar_reading)
- archive = rmtopen (ar_files[0], O_RDONLY | O_BINARY, 0666);
- else
- archive = rmtcreat (ar_files[0], 0666);
-
- if (archive < 0)
- {
- msg_perror ("can't open archive %s", ar_files[0]);
- exit (EX_BADARCH);
- }
-
- if (ar_reading)
- {
- for (;;)
- {
- char *ptr;
- int max, count;
-
- r_error_count = 0;
- error_loop:
- err = rmtread (archive, ar_block->charptr, (int) (blocksize));
- if (err < 0)
- {
- readerror ();
- goto error_loop;
- }
- if (err == 0)
- break;
- ptr = ar_block->charptr;
- max = err;
- while (max)
- {
- count = (max < RECORDSIZE) ? max : RECORDSIZE;
- err = write (STDOUT, ptr, count);
- if (err != count)
- {
- if (err < 0)
- {
- msg_perror ("can't write to compression program");
- exit (EX_SYSTEM);
- }
- else
- msg ("write to compression program short %d bytes",
- count - err);
- count = (err < 0) ? 0 : err;
- }
- ptr += count;
- max -= count;
- }
- }
- }
- else
- {
- for (;;)
- {
- int n;
- char *ptr;
-
- n = blocksize;
- ptr = ar_block->charptr;
- while (n)
- {
- err = read (STDIN, ptr, (n < RECORDSIZE) ? n : RECORDSIZE);
- if (err <= 0)
- break;
- n -= err;
- ptr += err;
- }
- /* EOF */
- if (err == 0)
- {
- if (!f_compress_block)
- blocksize -= n;
- else
- bzero (ar_block->charptr + blocksize - n, n);
- err = rmtwrite (archive, ar_block->charptr, blocksize);
- if (err != (blocksize))
- writeerror (err);
- if (!f_compress_block)
- blocksize += n;
- break;
- }
- if (n)
- {
- msg_perror ("can't read from compression program");
- exit (EX_SYSTEM);
- }
- err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
- if (err != blocksize)
- writeerror (err);
- }
- }
-
- /* close_archive(); */
- exit (0);
- }
- }
- /* So we should exec compress (-d) */
- if (ar_reading)
- execlp (f_compressprog, f_compressprog, "-d", (char *) 0);
- else
- execlp (f_compressprog, f_compressprog, (char *) 0);
- msg_perror ("can't exec %s", f_compressprog);
- _exit (EX_SYSTEM);
- }
-
-
- /* return non-zero if p is the name of a directory */
- int
- isfile (p)
- char *p;
- {
- struct stat stbuf;
-
- if (stat (p, &stbuf) < 0)
- return 1;
- if (S_ISREG (stbuf.st_mode))
- return 1;
- return 0;
- }
-
- #endif
-
- /*
- * Open an archive file. The argument specifies whether we are
- * reading or writing.
- */
- /* JF if the arg is 2, open for reading and writing. */
- void
- open_archive (reading)
- int reading;
- {
- msg_file = f_exstdout ? stderr : stdout;
-
- if (blocksize == 0)
- {
- msg ("invalid value for blocksize");
- exit (EX_ARGSBAD);
- }
-
- if (n_ar_files == 0)
- {
- msg ("No archive name given, what should I do?");
- exit (EX_BADARCH);
- }
-
- /*NOSTRICT*/
- if (f_multivol)
- {
- ar_block = (union record *) valloc ((unsigned) (blocksize + (2 * RECORDSIZE)));
- if (ar_block)
- ar_block += 2;
- }
- else
- ar_block = (union record *) valloc ((unsigned) blocksize);
- if (!ar_block)
- {
- msg ("could not allocate memory for blocking factor %d",
- blocking);
- exit (EX_ARGSBAD);
- }
-
- ar_record = ar_block;
- ar_last = ar_block + blocking;
- ar_reading = reading;
-
- if (f_multivol && f_verify)
- {
- msg ("cannot verify multi-volume archives");
- exit (EX_ARGSBAD);
- }
-
- if (f_compressprog)
- {
- if (reading == 2 || f_verify)
- {
- msg ("cannot update or verify compressed archives");
- exit (EX_ARGSBAD);
- }
- if (f_multivol)
- {
- msg ("cannot use multi-volume compressed archives");
- exit (EX_ARGSBAD);
- }
- child_open ();
- if (!reading && ar_files[0][0] == '-' && ar_files[0][1] == '\0')
- msg_file = stderr;
- /* child_open(rem_host, rem_file); */
- }
- else if (ar_files[0][0] == '-' && ar_files[0][1] == '\0')
- {
- f_reblock++; /* Could be a pipe, be safe */
- if (f_verify)
- {
- msg ("can't verify stdin/stdout archive");
- exit (EX_ARGSBAD);
- }
- if (reading == 2)
- {
- archive = STDIN;
- msg_file = stderr;
- write_archive_to_stdout++;
- }
- else if (reading)
- archive = STDIN;
- else
- {
- archive = STDOUT;
- msg_file = stderr;
- }
- }
- else if (reading == 2 || f_verify)
- {
- archive = rmtopen (ar_files[0], O_RDWR | O_CREAT | O_BINARY, 0666);
- }
- else if (reading)
- {
- archive = rmtopen (ar_files[0], O_RDONLY | O_BINARY, 0666);
- }
- else
- {
- archive = rmtcreat (ar_files[0], 0666);
- }
- if (archive < 0)
- {
- msg_perror ("can't open %s", ar_files[0]);
- exit (EX_BADARCH);
- }
- #ifndef __MSDOS__
- if (!_isrmt (archive))
- {
- struct stat tmp_stat;
-
- fstat (archive, &tmp_stat);
- if (S_ISREG (tmp_stat.st_mode))
- {
- ar_dev = tmp_stat.st_dev;
- ar_ino = tmp_stat.st_ino;
- }
- }
- #endif
-
- #ifdef __MSDOS__
- setmode (archive, O_BINARY);
- #endif
-
- if (reading)
- {
- ar_last = ar_block; /* Set up for 1st block = # 0 */
- (void) findrec (); /* Read it in, check for EOF */
-
- if (f_volhdr)
- {
- union record *head;
- #if 0
- char *ptr;
-
- if (f_multivol)
- {
- ptr = malloc (strlen (f_volhdr) + 20);
- sprintf (ptr, "%s Volume %d", f_volhdr, 1);
- }
- else
- ptr = f_volhdr;
- #endif
- head = findrec ();
- if (!head)
- {
- msg ("Archive not labelled to match %s", f_volhdr);
- exit (EX_BADVOL);
- }
- if (re_match (label_pattern, head->header.arch_name,
- strlen (head->header.arch_name), 0, 0) < 0)
- {
- msg ("Volume mismatch! %s!=%s", f_volhdr,
- head->header.arch_name);
- exit (EX_BADVOL);
- }
- #if 0
- if (strcmp (ptr, head->header.name))
- {
- msg ("Volume mismatch! %s!=%s", ptr, head->header.name);
- exit (EX_BADVOL);
- }
- if (ptr != f_volhdr)
- free (ptr);
- #endif
- }
- }
- else if (f_volhdr)
- {
- bzero ((void *) ar_block, RECORDSIZE);
- if (f_multivol)
- sprintf (ar_block->header.arch_name, "%s Volume 1", f_volhdr);
- else
- strcpy (ar_block->header.arch_name, f_volhdr);
- current_file_name = ar_block->header.arch_name;
- ar_block->header.linkflag = LF_VOLHDR;
- to_oct (time (0), 1 + 12, ar_block->header.mtime);
- finish_header (ar_block);
- /* ar_record++; */
- }
- }
-
-
- /*
- * Remember a union record * as pointing to something that we
- * need to keep when reading onward in the file. Only one such
- * thing can be remembered at once, and it only works when reading
- * an archive.
- *
- * We calculate "offset" then add it because some compilers end up
- * adding (baserec+ar_record), doing a 9-bit shift of baserec, then
- * subtracting ar_block from that, shifting it back, losing the top 9 bits.
- */
- void
- saverec (pointer)
- union record **pointer;
- {
- long offset;
-
- save_rec = pointer;
- offset = ar_record - ar_block;
- saved_recno = baserec + offset;
- }
-
- /*
- * Perform a write to flush the buffer.
- */
-
- /*send_buffer_to_file();
- if(new_volume) {
- deal_with_new_volume_stuff();
- send_buffer_to_file();
- }
- */
-
- void
- fl_write ()
- {
- int err;
- int copy_back;
- static long bytes_written = 0;
-
- if (f_checkpoint && !(++checkpoint % 10))
- msg ("Write checkpoint %d\n", checkpoint);
- if (tape_length && bytes_written >= tape_length * 1024)
- {
- errno = ENOSPC;
- err = 0;
- }
- else
- err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
- if (err != blocksize && !f_multivol)
- writeerror (err);
- else if (f_totals)
- tot_written += blocksize;
-
- if (err > 0)
- bytes_written += err;
- if (err == blocksize)
- {
- if (f_multivol)
- {
- if (!save_name)
- {
- real_s_name[0] = '\0';
- real_s_totsize = 0;
- real_s_sizeleft = 0;
- return;
- }
- #ifdef __MSDOS__
- if (save_name[1] == ':')
- save_name += 2;
- #endif
- while (*save_name == '/')
- save_name++;
-
- strcpy (real_s_name, save_name);
- real_s_totsize = save_totsize;
- real_s_sizeleft = save_sizeleft;
- }
- return;
- }
-
- /* We're multivol Panic if we didn't get the right kind of response */
- /* ENXIO is for the UNIX PC */
- if (err < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
- writeerror (err);
-
- /* If error indicates a short write, we just move to the next tape. */
-
- if (new_volume (0) < 0)
- return;
- bytes_written = 0;
- if (f_volhdr && real_s_name[0])
- {
- copy_back = 2;
- ar_block -= 2;
- }
- else if (f_volhdr || real_s_name[0])
- {
- copy_back = 1;
- ar_block--;
- }
- else
- copy_back = 0;
- if (f_volhdr)
- {
- bzero ((void *) ar_block, RECORDSIZE);
- sprintf (ar_block->header.arch_name, "%s Volume %d", f_volhdr, volno);
- to_oct (time (0), 1 + 12, ar_block->header.mtime);
- ar_block->header.linkflag = LF_VOLHDR;
- finish_header (ar_block);
- }
- if (real_s_name[0])
- {
- int tmp;
-
- if (f_volhdr)
- ar_block++;
- bzero ((void *) ar_block, RECORDSIZE);
- strcpy (ar_block->header.arch_name, real_s_name);
- ar_block->header.linkflag = LF_MULTIVOL;
- to_oct ((long) real_s_sizeleft, 1 + 12,
- ar_block->header.size);
- to_oct ((long) real_s_totsize - real_s_sizeleft,
- 1 + 12, ar_block->header.offset);
- tmp = f_verbose;
- f_verbose = 0;
- finish_header (ar_block);
- f_verbose = tmp;
- if (f_volhdr)
- ar_block--;
- }
-
- err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
- if (err != blocksize)
- writeerror (err);
- else if (f_totals)
- tot_written += blocksize;
-
-
- bytes_written = blocksize;
- if (copy_back)
- {
- ar_block += copy_back;
- bcopy ((void *) (ar_block + blocking - copy_back),
- (void *) ar_record,
- copy_back * RECORDSIZE);
- ar_record += copy_back;
-
- if (real_s_sizeleft >= copy_back * RECORDSIZE)
- real_s_sizeleft -= copy_back * RECORDSIZE;
- else if ((real_s_sizeleft + RECORDSIZE - 1) / RECORDSIZE <= copy_back)
- real_s_name[0] = '\0';
- else
- {
- #ifdef __MSDOS__
- if (save_name[1] == ':')
- save_name += 2;
- #endif
- while (*save_name == '/')
- save_name++;
-
- strcpy (real_s_name, save_name);
- real_s_sizeleft = save_sizeleft;
- real_s_totsize = save_totsize;
- }
- copy_back = 0;
- }
- }
-
- /* Handle write errors on the archive. Write errors are always fatal */
- /* Hitting the end of a volume does not cause a write error unless the write
- * was the first block of the volume */
-
- void
- writeerror (err)
- int err;
- {
- if (err < 0)
- {
- msg_perror ("can't write to %s", ar_files[cur_ar_file]);
- exit (EX_BADARCH);
- }
- else
- {
- msg ("only wrote %u of %u bytes to %s", err, blocksize, ar_files[cur_ar_file]);
- exit (EX_BADARCH);
- }
- }
-
- /*
- * Handle read errors on the archive.
- *
- * If the read should be retried, readerror() returns to the caller.
- */
- void
- readerror ()
- {
- # define READ_ERROR_MAX 10
-
- read_error_flag++; /* Tell callers */
-
- msg_perror ("read error on %s", ar_files[cur_ar_file]);
-
- if (baserec == 0)
- {
- /* First block of tape. Probably stupidity error */
- exit (EX_BADARCH);
- }
-
- /*
- * Read error in mid archive. We retry up to READ_ERROR_MAX times
- * and then give up on reading the archive. We set read_error_flag
- * for our callers, so they can cope if they want.
- */
- if (r_error_count++ > READ_ERROR_MAX)
- {
- msg ("Too many errors, quitting.");
- exit (EX_BADARCH);
- }
- return;
- }
-
-
- /*
- * Perform a read to flush the buffer.
- */
- void
- fl_read ()
- {
- int err; /* Result from system call */
- int left; /* Bytes left */
- char *more; /* Pointer to next byte to read */
-
- if (f_checkpoint && !(++checkpoint % 10))
- msg ("Read checkpoint %d\n", checkpoint);
-
- /*
- * Clear the count of errors. This only applies to a single
- * call to fl_read. We leave read_error_flag alone; it is
- * only turned off by higher level software.
- */
- r_error_count = 0; /* Clear error count */
-
- /*
- * If we are about to wipe out a record that
- * somebody needs to keep, copy it out to a holding
- * area and adjust somebody's pointer to it.
- */
- if (save_rec &&
- *save_rec >= ar_record &&
- *save_rec < ar_last)
- {
- record_save_area = **save_rec;
- *save_rec = &record_save_area;
- }
- if (write_archive_to_stdout && baserec != 0)
- {
- err = rmtwrite (1, ar_block->charptr, blocksize);
- if (err != blocksize)
- writeerror (err);
- }
- if (f_multivol)
- {
- if (save_name)
- {
- if (save_name != real_s_name)
- {
- #ifdef __MSDOS__
- if (save_name[1] == ':')
- save_name += 2;
- #endif
- while (*save_name == '/')
- save_name++;
-
- strcpy (real_s_name, save_name);
- save_name = real_s_name;
- }
- real_s_totsize = save_totsize;
- real_s_sizeleft = save_sizeleft;
-
- }
- else
- {
- real_s_name[0] = '\0';
- real_s_totsize = 0;
- real_s_sizeleft = 0;
- }
- }
-
- error_loop:
- err = rmtread (archive, ar_block->charptr, (int) blocksize);
- if (err == blocksize)
- return;
-
- if ((err == 0 || (err < 0 && errno == ENOSPC) || (err > 0 && !f_reblock)) && f_multivol)
- {
- union record *head;
-
- try_volume:
- if (new_volume ((cmd_mode == CMD_APPEND || cmd_mode == CMD_CAT || cmd_mode == CMD_UPDATE) ? 2 : 1) < 0)
- return;
- vol_error:
- err = rmtread (archive, ar_block->charptr, (int) blocksize);
- if (err < 0)
- {
- readerror ();
- goto vol_error;
- }
- if (err != blocksize)
- goto short_read;
-
- head = ar_block;
-
- if (head->header.linkflag == LF_VOLHDR)
- {
- if (f_volhdr)
- {
- #if 0
- char *ptr;
-
- ptr = (char *) malloc (strlen (f_volhdr) + 20);
- sprintf (ptr, "%s Volume %d", f_volhdr, volno);
- #endif
- if (re_match (label_pattern, head->header.arch_name,
- strlen (head->header.arch_name),
- 0, 0) < 0)
- {
- msg ("Volume mismatch! %s!=%s", f_volhdr,
- head->header.arch_name);
- --volno;
- --global_volno;
- goto try_volume;
- }
-
- #if 0
- if (strcmp (ptr, head->header.name))
- {
- msg ("Volume mismatch! %s!=%s", ptr, head->header.name);
- --volno;
- --global_volno;
- free (ptr);
- goto try_volume;
- }
- free (ptr);
- #endif
- }
- if (f_verbose)
- fprintf (msg_file, "Reading %s\n", head->header.arch_name);
- head++;
- }
- else if (f_volhdr)
- {
- msg ("Warning: No volume header!");
- }
-
- if (real_s_name[0])
- {
- long from_oct ();
-
- if (head->header.linkflag != LF_MULTIVOL || strcmp (head->header.arch_name, real_s_name))
- {
- msg ("%s is not continued on this volume!", real_s_name);
- --volno;
- --global_volno;
- goto try_volume;
- }
- if (real_s_totsize != from_oct (1 + 12, head->header.size) + from_oct (1 + 12, head->header.offset))
- {
- msg ("%s is the wrong size (%ld!=%ld+%ld)",
- head->header.arch_name, save_totsize,
- from_oct (1 + 12, head->header.size),
- from_oct (1 + 12, head->header.offset));
- --volno;
- --global_volno;
- goto try_volume;
- }
- if (real_s_totsize - real_s_sizeleft != from_oct (1 + 12, head->header.offset))
- {
- msg ("This volume is out of sequence");
- --volno;
- --global_volno;
- goto try_volume;
- }
- head++;
- }
- ar_record = head;
- return;
- }
- else if (err < 0)
- {
- readerror ();
- goto error_loop; /* Try again */
- }
-
- short_read:
- more = ar_block->charptr + err;
- left = blocksize - err;
-
- again:
- if (0 == (((unsigned) left) % RECORDSIZE))
- {
- /* FIXME, for size=0, multi vol support */
- /* On the first block, warn about the problem */
- if (!f_reblock && baserec == 0 && f_verbose && err > 0)
- {
- /* msg("Blocksize = %d record%s",
- err / RECORDSIZE, (err > RECORDSIZE)? "s": "");*/
- msg ("Blocksize = %d records", err / RECORDSIZE);
- }
- ar_last = ar_block + ((unsigned) (blocksize - left)) / RECORDSIZE;
- return;
- }
- if (f_reblock)
- {
- /*
- * User warned us about this. Fix up.
- */
- if (left > 0)
- {
- error2loop:
- err = rmtread (archive, more, (int) left);
- if (err < 0)
- {
- readerror ();
- goto error2loop; /* Try again */
- }
- if (err == 0)
- {
- msg ("archive %s EOF not on block boundary", ar_files[cur_ar_file]);
- exit (EX_BADARCH);
- }
- left -= err;
- more += err;
- goto again;
- }
- }
- else
- {
- msg ("only read %d bytes from archive %s", err, ar_files[cur_ar_file]);
- exit (EX_BADARCH);
- }
- }
-
-
- /*
- * Flush the current buffer to/from the archive.
- */
- void
- flush_archive ()
- {
- int c;
-
- baserec += ar_last - ar_block;/* Keep track of block #s */
- ar_record = ar_block; /* Restore pointer to start */
- ar_last = ar_block + blocking;/* Restore pointer to end */
-
- if (ar_reading)
- {
- if (time_to_start_writing)
- {
- time_to_start_writing = 0;
- ar_reading = 0;
-
- if (file_to_switch_to >= 0)
- {
- if ((c = rmtclose (archive)) < 0)
- msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
-
- archive = file_to_switch_to;
- }
- else
- (void) backspace_output ();
- fl_write ();
- }
- else
- fl_read ();
- }
- else
- {
- fl_write ();
- }
- }
-
- /* Backspace the archive descriptor by one blocks worth.
- If its a tape, MTIOCTOP will work. If its something else,
- we try to seek on it. If we can't seek, we lose! */
- int
- backspace_output ()
- {
- long cur;
- /* int er; */
- extern char *output_start;
-
- #ifdef MTIOCTOP
- struct mtop t;
-
- t.mt_op = MTBSR;
- t.mt_count = 1;
- if ((rmtioctl (archive, MTIOCTOP, &t)) >= 0)
- return 1;
- if (errno == EIO && (rmtioctl (archive, MTIOCTOP, &t)) >= 0)
- return 1;
- #endif
-
- cur = rmtlseek (archive, 0L, 1);
- cur -= blocksize;
- /* Seek back to the beginning of this block and
- start writing there. */
-
- if (rmtlseek (archive, cur, 0) != cur)
- {
- /* Lseek failed. Try a different method */
- msg ("Couldn't backspace archive file. It may be unreadable without -i.");
- /* Replace the first part of the block with nulls */
- if (ar_block->charptr != output_start)
- bzero (ar_block->charptr, output_start - ar_block->charptr);
- return 2;
- }
- return 3;
- }
-
-
- /*
- * Close the archive file.
- */
- void
- close_archive ()
- {
- int child;
- int status;
- int c;
-
- if (time_to_start_writing || !ar_reading)
- flush_archive ();
- if (cmd_mode == CMD_DELETE)
- {
- off_t pos;
-
- pos = rmtlseek (archive, 0L, 1);
- #ifndef __MSDOS__
- (void) ftruncate (archive, pos);
- #else
- (void) rmtwrite (archive, "", 0);
- #endif
- }
- if (f_verify)
- verify_volume ();
-
- if ((c = rmtclose (archive)) < 0)
- msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
-
- #ifndef __MSDOS__
- if (childpid)
- {
- /*
- * Loop waiting for the right child to die, or for
- * no more kids.
- */
- while (((child = wait (&status)) != childpid) && child != -1)
- ;
-
- if (child != -1)
- {
- if (WIFSIGNALED (status))
- {
- /* SIGPIPE is OK, everything else is a problem. */
- if (WTERMSIG (status) != SIGPIPE)
- msg ("child died with signal %d%s", WTERMSIG (status),
- WIFCOREDUMPED (status) ? " (core dumped)" : "");
- }
- else
- {
- /* Child voluntarily terminated -- but why? */
- if (WEXITSTATUS (status) == MAGIC_STAT)
- {
- exit (EX_SYSTEM); /* Child had trouble */
- }
- if (WEXITSTATUS (status) == (SIGPIPE + 128))
- {
- /*
- * /bin/sh returns this if its child
- * dies with SIGPIPE. 'Sok.
- */
- /* Do nothing. */
- }
- else if (WEXITSTATUS (status))
- msg ("child returned status %d",
- WEXITSTATUS (status));
- }
- }
- }
- #endif /* __MSDOS__ */
- }
-
-
- #ifdef DONTDEF
- /*
- * Message management.
- *
- * anno writes a message prefix on stream (eg stdout, stderr).
- *
- * The specified prefix is normally output followed by a colon and a space.
- * However, if other command line options are set, more output can come
- * out, such as the record # within the archive.
- *
- * If the specified prefix is NULL, no output is produced unless the
- * command line option(s) are set.
- *
- * If the third argument is 1, the "saved" record # is used; if 0, the
- * "current" record # is used.
- */
- void
- anno (stream, prefix, savedp)
- FILE *stream;
- char *prefix;
- int savedp;
- {
- # define MAXANNO 50
- char buffer[MAXANNO]; /* Holds annorecment */
- # define ANNOWIDTH 13
- int space;
- long offset;
- int save_e;
-
- save_e = errno;
- /* Make sure previous output gets out in sequence */
- if (stream == stderr)
- fflush (stdout);
- if (f_sayblock)
- {
- if (prefix)
- {
- fputs (prefix, stream);
- putc (' ', stream);
- }
- offset = ar_record - ar_block;
- (void) sprintf (buffer, "rec %d: ",
- savedp ? saved_recno :
- baserec + offset);
- fputs (buffer, stream);
- space = ANNOWIDTH - strlen (buffer);
- if (space > 0)
- {
- fprintf (stream, "%*s", space, "");
- }
- }
- else if (prefix)
- {
- fputs (prefix, stream);
- fputs (": ", stream);
- }
- errno = save_e;
- }
-
- #endif
-
- /* Called to initialize the global volume number. */
- void
- init_volume_number ()
- {
- FILE *vf;
-
- vf = fopen (f_volno_file, "r");
- if (!vf && errno != ENOENT)
- msg_perror ("%s", f_volno_file);
-
- if (vf)
- {
- fscanf (vf, "%d", &global_volno);
- fclose (vf);
- }
- }
-
- /* Called to write out the closing global volume number. */
- void
- closeout_volume_number ()
- {
- FILE *vf;
-
- vf = fopen (f_volno_file, "w");
- if (!vf)
- msg_perror ("%s", f_volno_file);
- else
- {
- fprintf (vf, "%d\n", global_volno);
- fclose (vf);
- }
- }
-
- /* We've hit the end of the old volume. Close it and open the next one */
- /* Values for type: 0: writing 1: reading 2: updating */
- int
- new_volume (type)
- int type;
- {
- int c;
- char inbuf[80];
- char *p;
- static FILE *read_file = 0;
- extern int now_verifying;
- extern char TTY_NAME[];
- static int looped = 0;
-
- if (!read_file && !f_run_script_at_end)
- read_file = (archive == 0) ? fopen (TTY_NAME, "r") : stdin;
-
- if (now_verifying)
- return -1;
- if (f_verify)
- verify_volume ();
- if ((c = rmtclose (archive)) < 0)
- msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
-
- global_volno++;
- volno++;
- cur_ar_file++;
- if (cur_ar_file == n_ar_files)
- {
- cur_ar_file = 0;
- looped = 1;
- }
-
- tryagain:
- if (looped)
- {
- /* We have to prompt from now on. */
- if (f_run_script_at_end)
- {
- closeout_volume_number ();
- system (info_script);
- }
- else
- for (;;)
- {
- fprintf (msg_file, "\007Prepare volume #%d for %s and hit return: ", global_volno, ar_files[cur_ar_file]);
- fflush (msg_file);
- if (fgets (inbuf, sizeof (inbuf), read_file) == 0)
- {
- fprintf (msg_file, "EOF? What does that mean?");
- if (cmd_mode != CMD_EXTRACT && cmd_mode != CMD_LIST && cmd_mode != CMD_DIFF)
- msg ("Warning: Archive is INCOMPLETE!");
- exit (EX_BADARCH);
- }
- if (inbuf[0] == '\n' || inbuf[0] == 'y' || inbuf[0] == 'Y')
- break;
-
- switch (inbuf[0])
- {
- case '?':
- {
- fprintf (msg_file, "\
- n [name] Give a new filename for the next (and subsequent) volume(s)\n\
- q Abort tar\n\
- ! Spawn a subshell\n\
- ? Print this list\n");
- }
- break;
-
- case 'q': /* Quit */
- fprintf (msg_file, "No new volume; exiting.\n");
- if (cmd_mode != CMD_EXTRACT && cmd_mode != CMD_LIST && cmd_mode != CMD_DIFF)
- msg ("Warning: Archive is INCOMPLETE!");
- exit (EX_BADARCH);
-
- case 'n': /* Get new file name */
- {
- char *q, *r;
- static char *old_name;
-
- for (q = &inbuf[1]; *q == ' ' || *q == '\t'; q++)
- ;
- for (r = q; *r; r++)
- if (*r == '\n')
- *r = '\0';
- old_name = p = (char *) malloc ((unsigned) (strlen (q) + 2));
- if (p == 0)
- {
- msg ("Can't allocate memory for name");
- exit (EX_SYSTEM);
- }
- (void) strcpy (p, q);
- ar_files[cur_ar_file] = p;
- }
- break;
-
- case '!':
- #ifdef __MSDOS__
- spawnl (P_WAIT, getenv ("COMSPEC"), "-", 0);
- #else
- /* JF this needs work! */
- switch (fork ())
- {
- case -1:
- msg_perror ("can't fork!");
- break;
- case 0:
- p = getenv ("SHELL");
- if (p == 0)
- p = "/bin/sh";
- execlp (p, "-sh", "-i", 0);
- msg_perror ("can't exec a shell %s", p);
- _exit (55);
- default:
- wait (0);
- break;
- }
- #endif
- break;
- }
- }
- }
-
-
- if (type == 2 || f_verify)
- archive = rmtopen (ar_files[cur_ar_file], O_RDWR | O_CREAT, 0666);
- else if (type == 1)
- archive = rmtopen (ar_files[cur_ar_file], O_RDONLY, 0666);
- else if (type == 0)
- archive = rmtcreat (ar_files[cur_ar_file], 0666);
- else
- archive = -1;
-
- if (archive < 0)
- {
- msg_perror ("can't open %s", ar_files[cur_ar_file]);
- goto tryagain;
- }
- #ifdef __MSDOS__
- setmode (archive, O_BINARY);
- #endif
- return 0;
- }
-
- /* this is a useless function that takes a buffer returned by wantbytes
- and does nothing with it. If the function called by wantbytes returns
- an error indicator (non-zero), this function is called for the rest of
- the file.
- */
- int
- no_op (size, data)
- int size;
- char *data;
- {
- return 0;
- }
-
- /* Some other routine wants SIZE bytes in the archive. For each chunk of
- the archive, call FUNC with the size of the chunk, and the address of
- the chunk it can work with.
- */
- int
- wantbytes (size, func)
- long size;
- int (*func) ();
- {
- char *data;
- long data_size;
-
- while (size)
- {
- data = findrec ()->charptr;
- if (data == NULL)
- { /* Check it... */
- msg ("Unexpected EOF on archive file");
- return -1;
- }
- data_size = endofrecs ()->charptr - data;
- if (data_size > size)
- data_size = size;
- if ((*func) (data_size, data))
- func = no_op;
- userec ((union record *) (data + data_size - 1));
- size -= data_size;
- }
- return 0;
- }
-